package com.eagle.core.servlet;

import com.alibaba.fastjson.JSON;
import com.eagle.core.annotation.*;
import com.eagle.core.aop.service.AopService;
import com.eagle.core.bean.SelfResponse;
import com.eagle.core.bean.View;
import com.eagle.core.utils.CommonUtils;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SelfDispatcherServlet extends HttpServlet {

	private Properties properties;
	private List<String> clazzNameList = new ArrayList<>();
	private Map<String, Object> ioc =  new HashMap<>(); //ioc容器
	private Map<String, Object> aop =  new HashMap<>(); //aop容器
	private List<Handler> handlerMapping =  new ArrayList<>();


	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		this.doPost(req,resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		//处理请求
		try{
			doDispatch(req,resp);
		}catch (Exception e){
			e.printStackTrace();
			resp.getWriter().write("500!! Server Exception");
		}

	}

	private Handler getHandler(HttpServletRequest req) {
		if(handlerMapping.isEmpty()){ return null;}
		//先拿到用户的请求
		String requestURI = req.getRequestURI();
		String contextPath = req.getContextPath();
		requestURI = requestURI.replace(contextPath,"").replaceAll("/+","/");

		for (Handler handler : handlerMapping) {
			Matcher matcher = handler.pattern.matcher(requestURI);
			if(matcher.matches()) {return handler;}
		}
		return  null;
	}

	private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {

		Handler handler = getHandler(req);
		if (handler == null) {
			resp.getWriter().write("404 not found");
			return;
		}
		//方法参数列表
		Class<?>[] parameterTypes = handler.method.getParameterTypes();
		Object[] paramValues = new Object[parameterTypes.length];

		Map<String, String[]> params = req.getParameterMap();
		for (Map.Entry<String, String[]> p : params.entrySet()) {
			String value = Arrays.toString(p.getValue()).replaceAll("\\[|\\]", "")
					.replaceAll(",\\s", ",");

			if(handler.paramIndexMapping.containsKey(p.getKey())){
				int idx = handler.paramIndexMapping.get(p.getKey());
				paramValues[idx] = convert(parameterTypes[idx], value);
			}
		}

		Integer reqIdx = handler.paramIndexMapping.get(HttpServletRequest.class.getName());
		Integer respIdx = handler.paramIndexMapping.get(HttpServletResponse.class.getName());
		paramValues[reqIdx] = req;
		paramValues[respIdx] = resp;
		//actionMethod是没有代理的
		Method actionMethod = handler.method;
		Object result = null;

		String controllerSimpleName = CommonUtils.toLowerFirstWord(handler.controller.getClass().getSimpleName());
		Object proxyControllerBean = this.aop.get(controllerSimpleName);
		//无代理，直接找Controller
		if(null == proxyControllerBean){
			//paramValues: 参数值
			result = actionMethod.invoke(handler.controller, paramValues);
		}else{
			actionMethod.setAccessible(true);
			result = actionMethod.invoke(proxyControllerBean, paramValues);
		}

		//跳转页面或返回json数据(这里类似于SpringMVC中的视图解析器)
		if (result == null) {
			return;
		} else if (result instanceof View){
			handleViewResult((View) result, req, resp);
		}else if (result instanceof SelfResponse) {
			handleDataResult((SelfResponse) result, resp);
		}

	}

	/**
	 * 跳转页面
	 */
	private void handleViewResult(View view, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
		String path = view.getPath();
		if (StringUtils.isNotEmpty(path)) {
			if (path.startsWith("/")) { //重定向
				response.sendRedirect(request.getContextPath() + path);
			} else { //请求转发
				Map<String, Object> model = view.getModel();
				for (Map.Entry<String, Object> entry : model.entrySet()) {
					request.setAttribute(entry.getKey(), entry.getValue());
				}
				request.getRequestDispatcher(getJspPath() + path).forward(request, response);
			}
		}
	}

	private String getJspPath(){
		String jspPath = properties.getProperty("jsp_path");
		if(StringUtils.isEmpty(jspPath)){
			jspPath = "/WEB-INF/view/";
		}
		return jspPath;
	}
	/**
	 * 返回JSON数据
	 */
	private void handleDataResult(SelfResponse resp, HttpServletResponse response) throws IOException {
		Object data = resp.getData();
		if (data != null) {
			response.setContentType("application/json");
			response.setCharacterEncoding("UTF-8");
			PrintWriter writer = response.getWriter();
			String json = JSON.toJSON(data).toString();
			writer.write(json);
			writer.flush();
			writer.close();
		}
	}

	@Override
	public void init(ServletConfig config) throws ServletException {
		//1.加载配置文件
		doLoadConfig(config.getInitParameter("contextConfigLocation"));

		//2 扫描用户设定的包下面所有的类
		doScan(properties.getProperty("scanPackage"));

		//3 .初始化所有相关联的类
		doInstance();

		//4 .初始化AOP
		initAop();

		//5 . 依赖注入（DI），拿到扫描到的类,通过反射机制,实例化,如果有代理类，则注入代理类
		// 并且放到ioc容器中(k-v  beanName-bean) beanName默认是首字母小写
		doAutowired();

		//6.初始化HandlerMapping(将url和method对应上)
		initHandleMapping();

		//7 获取ServletContext对象, 用于注册Servlet
		ServletContext servletContext = config.getServletContext();

		//8 注册处理jsp和静态资源的servlet(解决加载JSP时候，再次进入doDispatch而死循环的问题)
		registerServlet(servletContext);

		System.out.println("Self spring init done");


	}

	private void initAop() {
		if(ioc.isEmpty()) {
			System.out.println("warning: ioc is empty, not any aop init");
			return;
		}
		AopService aopService = new AopService(this.ioc,this.clazzNameList);
		try {
			//返回aop增强的容器
			this.aop = aopService.createProxy();
			System.out.println("init aop done...");
		} catch (Exception e) {
			e.printStackTrace();
			return;
		}
	}


	/**
	 * DefaultServlet和JspServlet都是由Web容器创建
	 * org.apache.catalina.servlets.DefaultServlet
	 * org.apache.jasper.servlet.JspServlet
	 */
	private void registerServlet(ServletContext servletContext) {
		//动态注册处理JSP的Servlet
		ServletRegistration jspServlet = servletContext.getServletRegistration("jsp");
		jspServlet.addMapping(getJspPath() + "*");

		//动态注册处理静态资源的默认Servlet
		ServletRegistration defaultServlet = servletContext.getServletRegistration("default");
		defaultServlet.addMapping("/favicon.ico"); //网站头像
		defaultServlet.addMapping(getJspPath() + "*");
	}

	private void initHandleMapping() {
		if(ioc.isEmpty()) {return;}
		for(Map.Entry<String,Object> entry : ioc.entrySet()){
			Class<?> clazz = entry.getValue().getClass();
			if(! clazz.isAnnotationPresent(SelfController.class)){continue;}
			String baseUrl = "";
			if(clazz.isAnnotationPresent(SelfRequestMapping.class)){
				SelfRequestMapping requestMapping = clazz.getAnnotation(SelfRequestMapping.class);
				baseUrl = requestMapping.value();
			}
			Method[] methods = clazz.getMethods();
			for (Method method : methods) {
				if (!method.isAnnotationPresent(SelfRequestMapping.class)) {
					continue;
				}
				SelfRequestMapping methodRequestMapping = method.getAnnotation(SelfRequestMapping.class);
				String regex = ("/" + baseUrl + "/" + methodRequestMapping.value()).
						replaceAll("/+","/");
				Pattern pattern = Pattern.compile(regex);
				Handler handler = new Handler(entry.getValue(), method, pattern);
				handlerMapping.add(handler);
				System.out.println("Mapped: "+regex +","+method);
			}
		}
	}

	private void doAutowired(){
		if(ioc.isEmpty()) {return;}
		for(Map.Entry<String,Object> entry : ioc.entrySet()){
			String beanFieldClassName = entry.getKey();
			Field[] fields = entry.getValue().getClass().getDeclaredFields();
			//遍历类下面的SelfAutowired注解
			for(Field field : fields){
				if(!field.isAnnotationPresent(SelfAutowired.class)){ continue;}
				SelfAutowired autowired = field.getAnnotation(SelfAutowired.class);
				String beanName = autowired.value();
				if("".equals(beanName)){
					beanName = field.getType().getName();
				}
				//以下依赖注入SelfAutowired
				//如果private/protected default时,对象是不可访问的，这里是强制授权访问
				field.setAccessible(true);
				/**
				 *  反射Field。如有有代理，则注入代理类，这里有两种情况
				 * proxyBean 当前类是否被代理，如果存在则用代理类反射
				 * fieldProxyBean 当前类的字段是否被代理，如果有则用字段的代理类赋值
				 */
				Object proxyBean = this.getProxyBean(beanFieldClassName); //beanFieldClassName=xxxController
				Object fieldProxyBean = this.getProxyBean(beanName); //fieldProxyBean = xxxService

				try{
					if(null != proxyBean){
						if(null != fieldProxyBean){
							Object iocBean = ioc.get(beanName);
							String aopBeanName = CommonUtils.toLowerFirstWord(iocBean.getClass().getSimpleName());
							field.set(proxyBean, aop.get(aopBeanName));
						} else {
							field.set(proxyBean, ioc.get(beanName));
						}
					} else {
						if(null != fieldProxyBean){
							Object iocBean = ioc.get(beanName);
							String aopBeanName = CommonUtils.toLowerFirstWord(iocBean.getClass().getSimpleName());
							field.set(entry.getValue(), aop.get(aopBeanName));
						} else {
							field.set(entry.getValue(), ioc.get(beanName));
						}
					}
				}catch (Exception e){
					e.printStackTrace();
				}
			}
		}
	}

	private Object getProxyBean(String beanName){
		Object proxyBean = this.aop.get(CommonUtils.toLowerFirstWord(beanName));
		if(null == proxyBean){
			Object iocBean = this.ioc.get(beanName);
			String simpleName = iocBean.getClass().getSimpleName();
			proxyBean = this.aop.get(CommonUtils.toLowerFirstWord(simpleName));
		}
		return proxyBean;
	}



	private void doInstance() {
		if(clazzNameList.isEmpty()) {return;}

		for (String clazzName : clazzNameList){
			try {
				Class<?> clazz = Class.forName(clazzName);

				if(clazz.isAnnotationPresent(SelfController.class)){
					Object obj = clazz.newInstance();
					ioc.put(CommonUtils.toLowerFirstWord(clazz.getSimpleName()), obj);
				}else if(clazz.isAnnotationPresent(SelfAspect.class)){
					Object obj = clazz.newInstance();
					ioc.put(CommonUtils.toLowerFirstWord(clazz.getSimpleName()), obj);
				}else if(clazz.isAnnotationPresent(SelfService.class)){
					//Service初始化并不是自己本身，而是对应的实现类
					//1 默认首字母小写作为beaname
					//2 如果指定beaname,则使用指定的
					SelfService annotation = clazz.getAnnotation(SelfService.class);
					String beanName =  annotation.value();
					if("".equals(beanName)){
						beanName = CommonUtils.toLowerFirstWord(clazz.getSimpleName());
					}
					Object obj = clazz.newInstance();
					ioc.put(beanName, obj);

					//3 获取赋值对象的接口，使用接口全名为key,实现类作为值
					Class<?>[] interfaces = clazz.getInterfaces();
					for(Class<?> i : interfaces){
						if(ioc.containsKey(i.getName())) {
							throw new Exception("The bean name "+i.getName()+" exist!!");
						}
						ioc.put(i.getName(), obj);
					}
				} else {
					continue;
				}
			} catch (Exception e) {
				e.printStackTrace();
				continue;
			}
		}

	}

	private void doScan(String packageName) {
		URL resource = this.getClass().getClassLoader().getResource("/"+packageName.replaceAll("\\.", "/"));

		File dir = new File(resource.getFile());
		for( File file: dir.listFiles()){
			if(file.isDirectory()){
				doScan(packageName+"."+file.getName());
			} else {
				String className = packageName+"."+file.getName().replace(".class", "");
				clazzNameList.add(className);
			}
		}
	}

	private void doLoadConfig(String location) {
		InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(location);
		properties = new Properties();
		try {
			properties.load(resourceAsStream);

		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			//关流
			if(null!=resourceAsStream){
				try {
					resourceAsStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

	}

	private Object convert(Class<?> type, String value){
		if(Integer.class == type){
			return  Integer.valueOf(value);
		}
		//TODO convert to other type
		return value;
	}

	private class Handler {
		protected Object controller;
		protected Method method;
		protected Pattern pattern;
		protected Map<String,Integer> paramIndexMapping;

		protected Handler(Object controller, Method method, Pattern pattern) {
			this.controller = controller;
			this.method = method;
			this.pattern = pattern;
			this.paramIndexMapping = new HashMap<>();
			putParamIndexMapping(method);
		}

		private void putParamIndexMapping(Method method) {
			Annotation[][] pa = method.getParameterAnnotations();
			//提取方法中加了注解的参数
			for (int i = 0; i < pa.length; i++) {
				for (Annotation a : pa[i]) {
					if(a instanceof SelfRequestParam){
						String paramName = ((SelfRequestParam) a).value();
						if(!"".equals(paramName.trim())){
							paramIndexMapping.put(paramName,i);
						}
					}
				}
			}

			//提取request和response的参数
			Class<?>[] parameterTypes = method.getParameterTypes();
			for (int i = 0; i < parameterTypes.length; i++) {
				Class<?> type = parameterTypes[i];
				if(type == HttpServletRequest.class || type == HttpServletResponse.class){
					paramIndexMapping.put(type.getName(),i);
				}
			}
		}
	}
}
